package npda;

import pda.PDAConfiguration;
import pda.PDARule;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author haoly
 * @title NPDARulebook
 * @date 2025/4/22 09:23
 * @description NPDA规则集
 */
public class NPDARulebook<T> {
    private final List<PDARule<T>> rules;

    public NPDARulebook(List<PDARule<T>> rules) {
        this.rules = new ArrayList<>(rules);
    }

    public Set<PDAConfiguration<T>> nextConfigurations(Set<PDAConfiguration<T>> configurations, Character character){
        Set<PDAConfiguration<T>> nextConfigs = new HashSet<>();
        for (PDAConfiguration<T> config : configurations) {
            Set<PDAConfiguration<T>> configsFromOneRule = followRulesFor(config, character);
            nextConfigs.addAll(configsFromOneRule);
        }
        return nextConfigs;
    }

    private Set<PDAConfiguration<T>> followRulesFor(PDAConfiguration<T> configuration, Character character) {
        Set<PDAConfiguration<T>> resultConfigs = new HashSet<>();
        List<PDARule<T>> applicableRules = rulesFor(configuration, character);
        for (PDARule<T> rule : applicableRules) {
            resultConfigs.add(rule.follow(configuration));
        }
        return resultConfigs;
    }

    public List<PDARule<T>> rulesFor(PDAConfiguration<T> configuration, Character character) {
        List<PDARule<T>> applicableRules = new ArrayList<>();
        for (PDARule<T> rule : rules) {
            if (rule.appliesTo(configuration, character)) {
                applicableRules.add(rule);
            }
        }
        return applicableRules;
    }

    public Set<PDAConfiguration<T>> followFreeMoves(Set<PDAConfiguration<T>> configurations){
        Set<PDAConfiguration<T>> moreConfigurations = nextConfigurations(configurations, null);
        if (configurations.containsAll(moreConfigurations)) {
            return configurations;
        }else {
            Set<PDAConfiguration<T>> combinedSet = new HashSet<>(configurations);
            combinedSet.addAll(moreConfigurations);
            return followFreeMoves(combinedSet);
        }
    }


}
